home *** CD-ROM | disk | FTP | other *** search
/ Windows Expert / Windows Expert.iso / windownt / wvnsrc75.zip / WVBLOCK.C < prev    next >
C/C++ Source or Header  |  1992-11-18  |  45KB  |  1,398 lines

  1. /* -- WVBLOCK.C --------------------------------------------------
  2.  *
  3.  *  This file contains a collection of routines to manipulate textblocks
  4.  *  and lines within textblocks.
  5.  *  The routines here view lines as atomic units only (they don't
  6.  *  look at the actual data in the lines).
  7.  *
  8.  *  Mark Riordan   20 September 1989.
  9.  */
  10.  
  11. #include "windows.h"
  12. #include "wvglob.h"
  13. #include "winvn.h"
  14. #include <ctype.h>
  15.  
  16. /*-- function NewBlock ------------------------------------------
  17.  *
  18.  *  Creates an empty, new textblock and links it into the list
  19.  *  of blocks after a given block.
  20.  *  After the call, both the old ("given") block and the new block
  21.  *  are locked in memory.
  22.  *
  23.  *    Entry    CurBlockPtr    points to a block
  24.  *
  25.  *    Exit     NewBlockPtr    points to a block that has been linked
  26.  *                            into the list just after CurBlockPtr.
  27.  *             Returns TRUE if couldn't allocate a block, else FALSE.
  28.  *             (I know I should fix that.)
  29.  */
  30. int
  31. NewBlock (CurBlockPtr, NewBlockPtr)
  32.      TypBlock far *CurBlockPtr, far ** NewBlockPtr;
  33. {
  34.   HANDLE hMyBlock;
  35.   TypBlock far *MyBlock, far * MyNextBlock;
  36.  
  37.   hMyBlock = GlobalAlloc (GMEM_MOVEABLE, (long) (CurBlockPtr->OwnerDoc->BlockSize));
  38.   if (hMyBlock)
  39.     {
  40.       MyBlock = (TypBlock far *) GlobalLock (hMyBlock);
  41.       SetupEmptyBlock (MyBlock, hMyBlock, CurBlockPtr->hCurBlock,
  42.                CurBlockPtr->hNextBlock, CurBlockPtr->OwnerDoc);
  43.       CurBlockPtr->hNextBlock = hMyBlock;
  44.  
  45.       /* Change the next block's "previous" pointer to point to us. */
  46.  
  47.       if (MyBlock->hNextBlock)
  48.     {
  49.       MyNextBlock = (TypBlock far *) GlobalLock (MyBlock->hNextBlock);
  50.       MyNextBlock->hPrevBlock = hMyBlock;
  51.       GlobalUnlock (MyBlock->hNextBlock);
  52.     }
  53.  
  54.       *NewBlockPtr = MyBlock;
  55.     }
  56.   else
  57.     {
  58.       MessageBox (CurBlockPtr->OwnerDoc->hDocWnd, "Could not allocate textblock", "Out of Memory Error", MB_OK | MB_ICONHAND);
  59.       return (1);
  60.     }
  61.   return (0);
  62. }
  63.  
  64. /*-- function SetupEmptyBlock -----------------------------------------
  65.  *
  66.  *  Initialize fields in a newly-allocated textblock.
  67.  *  Set the fields to indicate an empty block.
  68.  */
  69. void
  70. SetupEmptyBlock (BlockPtr, hCur, hPrev, hNext, DocPtr)
  71.      TypBlock far *BlockPtr;
  72.      HANDLE hCur, hPrev, hNext;
  73.      TypDoc *DocPtr;
  74. {
  75.   BlockPtr->OwnerDoc = DocPtr;
  76.   BlockPtr->hCurBlock = hCur;
  77.   BlockPtr->hPrevBlock = hPrev;
  78.   BlockPtr->hNextBlock = hNext;
  79.   BlockPtr->LWAp1 = sizeof (TypBlock) + sizeof (TypLine);
  80.   BlockPtr->NumLines = 0;
  81.   BlockPtr->eob = END_OF_BLOCK;
  82. #if 0
  83.   *((int far *) ((char far *) BlockPtr + sizeof (TypBlock))) = END_OF_BLOCK;
  84. #else
  85.   ((TypLine far *) ((char far *) BlockPtr + sizeof (TypBlock)))->length = END_OF_BLOCK;
  86.   ((TypLine far *) ((char far *) BlockPtr + sizeof (TypBlock)))->LineID = NextLineID++;
  87. #endif
  88. }
  89.  
  90. /*-- function DeleteBlock ---------------------------------------------
  91.  *
  92.  *  Delete a textblock from a document.
  93.  *
  94.  *    Entry    CurBlockPtr points to the block to delete.
  95.  *
  96.  *    Exit     CurBlockPtr points to the next block, if any, else the
  97.  *                         previous block.
  98.  *             CurLinePtr  points to the first line of the next block
  99.  *                         if there was one, else the last line of
  100.  *                         the previous block.
  101.  */
  102. BOOL
  103. DeleteBlock (TypBlock far ** CurBlockPtr, TypLine far ** CurLinePtr)
  104. {
  105.   TypBlock far *MyBlockPtr = *CurBlockPtr;
  106.   TypBlock far *BlockPtr;
  107.   HANDLE hMyBlock = MyBlockPtr->hCurBlock, hMyPrev = MyBlockPtr->hPrevBlock, hMyNext = MyBlockPtr->hNextBlock;
  108.   BOOL set_cur_block = FALSE;
  109.  
  110.   /* Don't delete the only block in the document. */
  111.   if (!hMyNext && !hMyPrev)
  112.     return (FALSE);
  113.  
  114.   /* Update the prev pointer of the next block to point to
  115.    * the block previous to the one being deleted.
  116.    */
  117.  
  118.   if (hMyNext)
  119.     {
  120.       BlockPtr = (TypBlock far *) GlobalLock (hMyNext);
  121.       BlockPtr->hPrevBlock = hMyPrev;
  122.       GlobalUnlock (hMyNext);
  123.  
  124.       NextLine (CurBlockPtr, CurLinePtr);
  125.       set_cur_block = TRUE;
  126.     }
  127.   else
  128.     {
  129.       /* The block we are deleting has no next block, so it
  130.        * must be the last in the document.  Update document pointers.
  131.        */
  132.       MyBlockPtr->OwnerDoc->hLastBlock = hMyPrev;
  133.     }
  134.  
  135.   /* Update the next pointer of the previous block to point to
  136.    * the block after the one being deleted.
  137.    */
  138.  
  139.   if (hMyPrev)
  140.     {
  141.       BlockPtr = (TypBlock far *) GlobalLock (hMyPrev);
  142.       BlockPtr->hNextBlock = hMyNext;
  143.       GlobalUnlock (hMyPrev);
  144.  
  145.       if (!set_cur_block)
  146.     {
  147.       /* There is no next block, so we want to position the
  148.            * pointer to the end of the previous block.
  149.            */
  150.       PrevLine (CurBlockPtr, CurLinePtr);
  151.       NextLine (CurBlockPtr, CurLinePtr);
  152.     }
  153.     }
  154.   else
  155.     {
  156.       /* The block we are deleting has no previous block, so it
  157.        * must be the first in the document.  Update document pointers.
  158.        */
  159.       MyBlockPtr->OwnerDoc->hFirstBlock = hMyNext;
  160.     }
  161.  
  162.   GlobalFree (hMyBlock);
  163.   return (TRUE);
  164. }
  165.  
  166. /*-- function AddLine -------------------------------------------------
  167.  *
  168.  *  Add a line to a textblock.  Create a new textblock if necessary.
  169.  *
  170.  *    Entry    LineToAdd   points to a line to add.
  171.  *             CurBlockPtr points to the block to which we want to add it.
  172.  *             CurAddPtr   points to the place in the block to add the line.
  173.  *
  174.  *    Exit     CurBlockPtr & CurAddPtr point to right after the
  175.  *                newly-added line.
  176.  *
  177.  *    Method   There are three cases:
  178.  *             1. The new line will fit in the textblock.
  179.  *                That's pretty easy.
  180.  *             2. The new line won't fit, and we are at the end of
  181.  *                the current block.
  182.  *                I could have just treated this as I do case 3 (splitting
  183.  *                the textblock), but that would have resulted in
  184.  *                a lot of unnecessary internal fragmentation
  185.  *                (i.e., wasted space inside textblocks).
  186.  *                So, what I do is leave the contents of the old block
  187.  *                alone and just create a new empty block, to which
  188.  *                the line is added by a recursive call.
  189.  *             3. The new line won't fit, and we are not at the end of
  190.  *                the textblock.
  191.  *                In this case, I split the textblock at the first line
  192.  *                boundary after the split point (the split point is
  193.  *                an attribute of the document--usually about 2/3 of the
  194.  *                way through the textblock), creating two semi-full textblocks
  195.  *                where there was one full one before.
  196.  *                The routine then recursively calls itself.
  197.  */
  198. int
  199. AddLine (LineToAdd, CurBlockPtr, CurAddPtr)
  200.      TypLine *LineToAdd;
  201.      TypBlock far **CurBlockPtr;
  202.      TypLine far **CurAddPtr;
  203. {
  204.   TypBlock far *MyBlock = *CurBlockPtr;
  205.  
  206.   int left = (MyBlock->OwnerDoc->BlockSize - MyBlock->LWAp1);
  207.  
  208.   if (LineToAdd->length <= (int) left)
  209.     {
  210.  
  211.       /* There's room in the current block for this line, so just    */
  212.       /* move lines down to accomodate this line and copy it in.     */
  213.  
  214.       MoveBytes ((char far *) *CurAddPtr,
  215.          ((char far *) *CurAddPtr) + LineToAdd->length,
  216.        (((char far *) MyBlock + MyBlock->LWAp1)) - (char far *) *CurAddPtr);
  217.       MoveBytes ((char far *) LineToAdd, (char far *) *CurAddPtr,
  218.          LineToAdd->length);
  219.  
  220.       /* Adjust textblock counters.                                  */
  221.       MyBlock->LWAp1 += LineToAdd->length;
  222.       MyBlock->NumLines++;
  223.       IncPtr ((*CurAddPtr), LineToAdd->length);
  224.       MyBlock->OwnerDoc->TotalLines++;
  225.     }
  226.   else
  227.     {
  228.  
  229.       /* There isn't enough room in the current textblock.           */
  230.       /* If we are at the end of the current block, just create      */
  231.       /* a new empty one; otherwise, split the current block.        */
  232.  
  233.       TypBlock far *NewBlockPtr;
  234.  
  235.       if ((*CurAddPtr)->length == END_OF_BLOCK)
  236.     {
  237.       /* We're at end of block; create a new empty one.           */
  238.       /* This will be the current block, so release references    */
  239.       /* to the now-current block                                 */
  240.       if (NewBlock (MyBlock, CurBlockPtr))
  241.         {
  242.           return (1);
  243.         }
  244.       else
  245.         {
  246.           GlobalUnlock (MyBlock->hCurBlock);
  247.           *CurAddPtr = (TypLine far *)
  248.         ((char far *) *CurBlockPtr + sizeof (TypBlock));
  249.           /* Now we can recursively try again to add the line.     */
  250.           if (AddLine (LineToAdd, CurBlockPtr, CurAddPtr))
  251.         {
  252.           return (1);
  253.         }
  254.         }
  255.     }
  256.       else
  257.     {
  258.       /* We need to split the textblock.                          */
  259.       /* Find a place to split the block by starting at the       */
  260.       /* beginning of the block and skipping through the lines    */
  261.       /* until we pass the number of bytes that marks the         */
  262.       /* split point.  The previous line is the split point.      */
  263.  
  264.       TypLine far *MyLinePtr = (TypLine far *) ((char far *) MyBlock + sizeof (TypBlock));
  265.       TypLine far *MyLastLine = MyLinePtr;
  266.       int nOldLines = 0, nBytesMoved, MyAddOffset;
  267.  
  268.       while ((char far *) MyLinePtr - (char far *) MyBlock < MyBlock->OwnerDoc->SplitSize &&
  269.          MyLinePtr->length != END_OF_BLOCK)
  270.         {
  271.           nOldLines++;
  272.           MyLastLine = MyLinePtr;
  273.           IncPtr (MyLinePtr, MyLinePtr->length);
  274.         }
  275.  
  276.       /* Allocate the new block and copy the last portion of      */
  277.       /* the current block to the new one.   The range to         */
  278.       /* copy starts at the above-determined split point and      */
  279.       /* goes until the LWA+1.                                    */
  280.       /* Then adjust the new & old textblock fields.              */
  281.  
  282.       if (NewBlock (MyBlock, &NewBlockPtr))
  283.         {
  284.           return (1);
  285.         }
  286.       else
  287.         {
  288.           MoveBytes (MyLastLine, (char far *) NewBlockPtr + sizeof (TypBlock),
  289.              nBytesMoved = (((char far *) MyBlock + MyBlock->LWAp1)) - (char far *) MyLastLine);
  290.           MyBlock->LWAp1 = (char far *) MyLastLine - ((char far *) MyBlock)
  291.         + sizeof (TypLine);
  292.           ((TypLine far *) MyLastLine)->length = END_OF_BLOCK;
  293.           ((TypLine far *) MyLastLine)->LineID = NextLineID++;
  294.           NewBlockPtr->NumLines = MyBlock->NumLines - nOldLines;
  295.           MyBlock->NumLines = nOldLines;
  296.           NewBlockPtr->LWAp1 = nBytesMoved + sizeof (TypBlock);
  297.  
  298.           /* Should the new line go in the old block or the new?   */
  299.           /* If the add point is beyond the end of the newly-      */
  300.           /* truncated block, we must move the add point to the    */
  301.           /* next block and make the new block the current one.    */
  302.           /* The new position should be the same                   */
  303.           /* number of line bytes past the beginning of the next   */
  304.           /* block as it was past the split point when it was in   */
  305.           /* the old block.                                        */
  306.           /* Either way, one block (the old or the new one)        */
  307.           /* must be unlocked.                                     */
  308.  
  309.           if (*CurAddPtr >= MyLastLine)
  310.         {
  311.           /* Add point is in new block.                         */
  312.  
  313.           MyAddOffset = (char far *) *CurAddPtr - (char far *) MyLastLine + sizeof (TypBlock);
  314.           *CurAddPtr = (TypLine far *) ((char far *) NewBlockPtr + MyAddOffset);
  315.           *CurBlockPtr = NewBlockPtr;
  316.           GlobalUnlock (MyBlock->hCurBlock);
  317.         }
  318.           else
  319.         {
  320.           /* Add point is in current block.                     */
  321.           GlobalUnlock (NewBlockPtr->hCurBlock);
  322.         }
  323.           return (AddLine (LineToAdd, CurBlockPtr, CurAddPtr));
  324.         }
  325.     }
  326.     }
  327.   return (0);
  328. }
  329.  
  330. /*-- function ReplaceLine -------------------------------------------------
  331.  *
  332.  *  Replace a line in a textblock.  Create a new textblock if necessary.
  333.  *
  334.  *    Entry    LineToAdd   is the line to put into a textblock.
  335.  *             CurBlockPtr points to the block containing the old copy.
  336.  *             CurLinePtr  points to the old copy of the line.
  337.  *
  338.  *    Exit     returns TRUE if successful.
  339.  *             CurBlockPtr points to the block containing the new copy of the line.
  340.  *             CurLinePtr  points to the new copy of the line.
  341.  *                         Usually, CurBlockPtr & CurAddPtr will be the
  342.  *                         same upon exit as upon entry; however, sometimes
  343.  *                         a textblock split is necessary.
  344.  *
  345.  *    Method   There are two cases:
  346.  *             1. There is enough room in this textblock for the
  347.  *                changed line.
  348.  *                This is pretty simple.
  349.  *             2. There is not enough room in this textblock for the
  350.  *                changed line.  This requires a textblock split,
  351.  *                as with AddLine.  There's a lot of common code
  352.  *                here--should probably consolidate it in a single routine
  353.  *                some day.
  354.  */
  355. BOOL
  356. ReplaceLine (LineToAdd, CurBlockPtr, CurLinePtr)
  357.      TypLine *LineToAdd;
  358.      TypBlock far **CurBlockPtr;
  359.      TypLine far **CurLinePtr;
  360. {
  361.   TypBlock far *MyBlockPtr = *CurBlockPtr;
  362.   TypLine far *MyLinePtr = *CurLinePtr;
  363.   int deltasize;
  364.   int numbytes;
  365.   char far *target, far * source;
  366.  
  367.   deltasize = LineToAdd->length - (MyLinePtr->length);
  368.  
  369.   if (deltasize <= (int) (MyBlockPtr->OwnerDoc->BlockSize - MyBlockPtr->LWAp1))
  370.     {
  371.  
  372.       /* There's room in the current block for this line, so just    */
  373.       /* move lines down to accomodate this line and copy it in.     */
  374.       /* Move the data in the textblock up or down, starting with    */
  375.       /* the line after the line being replaced.                     */
  376.  
  377.       source = (char far *) MyLinePtr + MyLinePtr->length;
  378.       target = source + deltasize;
  379.       numbytes = ((char far *) MyBlockPtr + MyBlockPtr->LWAp1) -
  380.     (char far *) MyLinePtr - MyLinePtr->length;
  381. #if 0
  382.       numbytes = (char far *) MyBlockPtr + MyBlockPtr->LWAp1 - (char far *) MyLinePtr;
  383.       target = (char far *) MyLinePtr + deltasize;
  384. #endif
  385.       MoveBytes (source, target, numbytes);
  386.  
  387.       MoveBytes ((char far *) LineToAdd, (char far *) MyLinePtr,
  388.          LineToAdd->length);
  389.       MyBlockPtr->LWAp1 += deltasize;
  390.     }
  391.   else
  392.     {
  393.  
  394.       /* There isn't enough room in the current textblock.           */
  395.       /* We need to split the textblock.                          */
  396.       /* Find a place to split the block by starting at the       */
  397.       /* beginning of the block and skipping through the lines    */
  398.       /* until we pass the number of bytes that marks the         */
  399.       /* split point.  The previous line is the split point.      */
  400.  
  401.       TypBlock far *NewBlockPtr;
  402.  
  403.       TypLine far *MyLinePtr = (TypLine far *) ((char far *) MyBlockPtr + sizeof (TypBlock));
  404.       TypLine far *MyLastLine = MyLinePtr;
  405.       int nOldLines = 0, nBytesMoved, MyAddOffset;
  406.  
  407. #if 0
  408.       MessageBox (hWndConf, "Need to split textblock", "in ReplaceLine", MB_OK);
  409. #endif
  410.  
  411.       while (((char far *) MyLinePtr - (char far *) MyBlockPtr) < MyBlockPtr->OwnerDoc->SplitSize &&
  412.          MyLinePtr->length != END_OF_BLOCK)
  413.     {
  414.       nOldLines++;
  415.       MyLastLine = MyLinePtr;
  416. /*         (char far *) MyLinePtr += MyLinePtr->length; */
  417.       IncPtr (MyLinePtr, MyLinePtr->length);
  418.     }
  419.  
  420.       /* Allocate the new block and copy the last portion of      */
  421.       /* the current block to the new one.   The range to         */
  422.       /* copy starts at the above-determined split point and      */
  423.       /* goes until the LWA+1.                                    */
  424.       /* Then adjust the new & old textblock fields.              */
  425.  
  426.       if (NewBlock (MyBlockPtr, &NewBlockPtr))
  427.     {
  428.       return (1);
  429.     }
  430.       else
  431.     {
  432.       MoveBytes (MyLastLine, (char far *) NewBlockPtr + sizeof (TypBlock),
  433.              nBytesMoved = (((char far *) MyBlockPtr + MyBlockPtr->LWAp1)) - (char far *) MyLastLine);
  434.       MyBlockPtr->LWAp1 = (char far *) MyLastLine - ((char far *) MyBlockPtr)
  435.         + sizeof (TypLine);
  436.       ((TypLine far *) MyLastLine)->length = END_OF_BLOCK;
  437.       ((TypLine far *) MyLastLine)->LineID = NextLineID++;
  438.       NewBlockPtr->NumLines = MyBlockPtr->NumLines - nOldLines;
  439.       MyBlockPtr->NumLines = nOldLines;
  440.       NewBlockPtr->LWAp1 = nBytesMoved + sizeof (TypBlock);
  441.  
  442.       /* Should this line go in the old block or the new?   */
  443.       /* If the add point is beyond the end of the newly-      */
  444.       /* truncated block, we must move the add point to the    */
  445.       /* next block and make the new block the current one.    */
  446.       /* The new position should be the same                   */
  447.       /* number of line bytes past the beginning of the next   */
  448.       /* block as it was past the split point when it was in   */
  449.       /* the old block.                                        */
  450.       /* Either way, one block (the old or the new one)        */
  451.       /* must be unlocked.                                     */
  452.  
  453.       if (*CurLinePtr >= MyLastLine)
  454.         {
  455.           /* Replace point is in new block.                         */
  456.  
  457.           MyAddOffset = (char far *) *CurLinePtr - (char far *) MyLastLine + sizeof (TypBlock);
  458.           *CurLinePtr = (TypLine far *) ((char far *) NewBlockPtr + MyAddOffset);
  459.           *CurBlockPtr = NewBlockPtr;
  460.           GlobalUnlock (MyBlockPtr->hCurBlock);
  461.         }
  462.       else
  463.         {
  464.           /* Add point is in current block.                     */
  465.           GlobalUnlock (NewBlockPtr->hCurBlock);
  466.         }
  467.       return (ReplaceLine (LineToAdd, CurBlockPtr, CurLinePtr));
  468.     }
  469.     }
  470.   return (TRUE);
  471. }
  472.  
  473. /*-- function DeleteLine ----------------------------------------------
  474.  *
  475.  *  Delete a line from a textblock.
  476.  *
  477.  *    Entry    CurBlockPtr points to the block containing the line.
  478.  *             CurLinePtr  points to the line to delete.
  479.  *
  480.  *    Exit     returns TRUE if successful.
  481.  *             CurBlockPtr points to the block containing the next line,
  482.  *                         if any.
  483.  *             CurLinePtr  points to the next line, if any--else the
  484.  *                         end of the block.
  485.  *                         Usually, CurBlockPtr & CurLinePtr will be the
  486.  *                         same upon exit as upon entry; however, sometimes
  487.  *                         a textblock is emptied and the entire block
  488.  *                         is deleted.
  489.  */
  490. BOOL
  491. DeleteLine (TypBlock far ** CurBlockPtr, TypLine far ** CurLinePtr)
  492. {
  493.   TypBlock far *MyBlockPtr = *CurBlockPtr;
  494.   TypLine far *MyLinePtr = *CurLinePtr;
  495.   int bytes_to_end, bytes_to_copy;
  496.   int cur_length = MyLinePtr->length;
  497.  
  498.   /* If we are (erroneously) at the end of a block, do nothing */
  499.  
  500.   if (MyLinePtr->length == END_OF_BLOCK)
  501.     {
  502.       return (FALSE);
  503.     }
  504.  
  505.   /* Copy the remainder of the block on top of the line to be deleted. */
  506.  
  507.   bytes_to_end = ((char far *) MyBlockPtr + MyBlockPtr->LWAp1) -
  508.     (char far *) MyLinePtr;
  509.   bytes_to_copy = bytes_to_end - cur_length;
  510.   MoveBytes ((char far *) MyLinePtr + cur_length, (char far *) MyLinePtr,
  511.          bytes_to_copy);
  512.  
  513.   /* Update the block counters.  */
  514.  
  515.   MyBlockPtr->LWAp1 -= cur_length;
  516.   MyBlockPtr->NumLines--;
  517.   MyBlockPtr->OwnerDoc->TotalLines--;
  518.  
  519.   /* If we are now at the end of the block, we are faced with one of
  520.    * two situations:
  521.    * 1.  We are also at the beginning of the block, and hence
  522.    *     the block is empty.  In this case we must delete the block
  523.    *     unless it is the only block.
  524.    * 2.  Otherwise, we must advance to the next block, if any.
  525.    */
  526.  
  527.   if (MyLinePtr->length == END_OF_BLOCK)
  528.     {
  529.  
  530.       /* We are at the end of the block.  */
  531.  
  532.       if (*((int far *) (MyLinePtr) - 1) == END_OF_BLOCK)
  533.     {
  534.  
  535.       /* We have emptied the block.  We must check for whether
  536.            * this is the last block in the document.
  537.            */
  538.       if (MyBlockPtr->OwnerDoc->TotalLines)
  539.         {
  540.           /* The document is not empty.  Delete this empty block.
  541.                */
  542.           DeleteBlock (CurBlockPtr, CurLinePtr);
  543.         }
  544.       else
  545.         {
  546.           /* The document is empty.  Don't delete this block.
  547.                * Leave the pointer at the same place.  It is now pointing
  548.                * at the next line.
  549.                */
  550.         }
  551.     }
  552.       else
  553.     {
  554.       /* We're at the end of the block, but the block is not empty.
  555.            * Just advance to the next block, if any.
  556.            */
  557.       NextLine (CurBlockPtr, CurLinePtr);
  558.     }
  559.     }
  560.  
  561.   return (TRUE);
  562. }
  563.  
  564.  
  565. /*-- function NextLine ------------------------------------------------
  566.  *
  567.  *  Advance a pointer to point to the next line in a document.
  568.  *
  569.  *    Entry    BlockPtr    points to the current block.
  570.  *             LinePtr     points to the current line.
  571.  *
  572.  *    Exit     BlockPtr & LinePtr point to the next line, if there
  573.  *              was one.
  574.  *             Returns TRUE iff pointer was moved.
  575.  *
  576.  *    Method   Must advance BlockPtr if LinePtr was at the end
  577.  *             of a block to start with, or arrives at the end of a block
  578.  *             after moving to the end of the current line.
  579.  */
  580. int
  581. NextLine (BlockPtr, LinePtr)
  582.      TypBlock far **BlockPtr;
  583.      TypLine far **LinePtr;
  584. {
  585.   BOOL retcode = 0;
  586.  
  587.   if ((*LinePtr)->length != END_OF_BLOCK)
  588.     {
  589. /*      (char far *) *LinePtr += (*LinePtr)->length; */
  590.       IncPtr (*LinePtr, (*LinePtr)->length);
  591.     }
  592.   if ((*LinePtr)->length == END_OF_BLOCK)
  593.     {
  594.       if ((*BlockPtr)->hNextBlock)
  595.     {
  596.       GlobalUnlock ((*BlockPtr)->hCurBlock);
  597.       *BlockPtr = (TypBlock far *) GlobalLock ((*BlockPtr)->hNextBlock);
  598.       *LinePtr = (TypLine far *) ((char far *) *BlockPtr + sizeof (TypBlock));
  599.       retcode = 1;
  600.     }
  601.     }
  602.   else
  603.     {
  604.       retcode = 1;
  605.     }
  606.   return (retcode);
  607. }
  608.  
  609. /*-- function PrevLine ------------------------------------------------
  610.  *
  611.  *  Back up a pointer to point to the next line in a document.
  612.  *
  613.  *    Entry    BlockPtr    points to the current block.
  614.  *             LinePtr     points to the current line.
  615.  *
  616.  *    Exit     BlockPtr & LinePtr point to the previous line, if there
  617.  *              was one.
  618.  *             Returns TRUE iff pointer was moved.
  619.  *
  620.  *    Method   Rely on the fact that the last field of the previous
  621.  *             line is the length of that line.  Also, the last field
  622.  *             in a textblock header (which is what you get if you
  623.  *             try to look at the previous line in a textblock if you're
  624.  *             at the beginning of a textblock) has the value END_OF_BLOCK.
  625.  */
  626. int
  627. PrevLine (BlockPtr, LinePtr)
  628.      TypBlock far **BlockPtr;
  629.      TypLine far **LinePtr;
  630. {
  631.   if (*((int far *) (*LinePtr) - 1) != END_OF_BLOCK)
  632.     {
  633. /*      (char far *) *LinePtr -= *((int far *)(*LinePtr)-1); */
  634.       IncPtr (*LinePtr, -(*((int far *) (*LinePtr) - 1)));
  635.     }
  636.   else
  637.     {
  638.       if ((*BlockPtr)->hPrevBlock)
  639.     {
  640.       GlobalUnlock ((*BlockPtr)->hCurBlock);
  641.       *BlockPtr = (TypBlock far *) GlobalLock ((*BlockPtr)->hPrevBlock);
  642.       *LinePtr = (TypLine far *)
  643.         ((char far *) *BlockPtr + (*BlockPtr)->LWAp1 - sizeof (TypLine));
  644. /*         (char far *) *LinePtr -= ( *((int far *)(*LinePtr)-1)); */
  645.       IncPtr (*LinePtr, -(*((int far *) (*LinePtr) - 1)));
  646.     }
  647.       else
  648.     {
  649.       return (0);
  650.     }
  651.     }
  652.   return (1);
  653. }
  654.  
  655. /*--- function TopOfDoc ------------------------------------------------
  656.  *
  657.  *   Set pointers to the first line of a document.
  658.  *
  659.  *    Entry    Doc         points to a document.
  660.  *
  661.  *    Exit     BlockPtr, LinePtr   point to the first
  662.  *                         line in the document.  This line is locked.
  663.  */
  664. void
  665. TopOfDoc (Doc, BlockPtr, LinePtr)
  666.      TypDoc *Doc;
  667.      TypBlock far **BlockPtr;
  668.      TypLine far **LinePtr;
  669. {
  670.   HANDLE hBlock;
  671.   int Offset;
  672.   TypLineID MyLineID;
  673.  
  674.   hBlock = Doc->hFirstBlock;
  675.   Offset = sizeof (TypBlock);
  676.   MyLineID = 0L;
  677.   LockLine (hBlock, Offset, MyLineID, BlockPtr, LinePtr);
  678. }
  679.  
  680. /*--- function ExtractTextLine -----------------------------------------------
  681.  *
  682.  *  Extract the text portion of a line to another buffer.
  683.  *
  684.  *    Entry    Doc      points to the document.
  685.  *             LinePtr  points to a line.
  686.  *             BufSize  is the size of the output buffer in bytes.
  687.  *
  688.  *    Exit     Buf      contains the text, terminated by a zero byte.
  689.  *             Returns FALSE iff no text could be extracted
  690.  *               (not a valid line).
  691.  */
  692. BOOL
  693. ExtractTextLine (Doc, LinePtr, Buf, BufSize)
  694.      TypDoc *Doc;
  695.      TypLine far *LinePtr;
  696.      char *Buf;
  697.      int BufSize;
  698. {
  699.   char far *bptr;
  700.   BOOL DidIt = FALSE;
  701.  
  702.   if (LinePtr->length == END_OF_BLOCK)
  703.     {
  704.     }
  705.   else
  706.     {
  707.       if (Doc->DocType == DOCTYPE_NET)
  708.     {
  709.       bptr = (char far *) LinePtr + sizeof (TypLine) + sizeof (TypGroup);
  710.       while (--BufSize > 1 && (*(Buf++) = *(bptr++)));
  711.       *Buf = '\0';
  712.       DidIt = TRUE;
  713.     }
  714.       else if (Doc->DocType == DOCTYPE_ARTICLE)
  715.     {
  716.       bptr = (char far *) LinePtr + sizeof (TypLine) + sizeof (TypText);
  717.       while (--BufSize > 1 && (*(Buf++) = *(bptr++)));
  718.       *Buf = '\0';
  719.       DidIt = TRUE;
  720.     }
  721.     }
  722.   return (DidIt);
  723. }
  724.  
  725. /*-- function LockLine ---------------------------------------------
  726.  *
  727.  *  Find the specified line, and return a pointer to it.
  728.  *  Lock the line in memory.
  729.  *
  730.  *  Entry:  hBlock      is the handle of a block we think contains
  731.  *                      the desired line.
  732.  *          LineOff     is the offset in bytes from the beginning of
  733.  *                      the block for where we think the line is.
  734.  *          FindLineID  is the LineID of the desired line.
  735.  *                      If it is 0, we don't check line ID's (don't care).
  736.  *
  737.  *  Exit:   returns     TRUE iff the line was found.
  738.  *          BlockPtr    points to the beginning of the block
  739.  *                      in which the line was found.
  740.  *          LinePtr     points to the line.
  741.  */
  742. BOOL
  743. LockLine (hBlock, LineOff, FindLineID, BlockPtr, LinePtr)
  744.      HANDLE hBlock;
  745.      unsigned int LineOff;
  746.      TypLineID FindLineID;
  747.      TypBlock far **BlockPtr;
  748.      TypLine far **LinePtr;
  749. {
  750.   *BlockPtr = (TypBlock far *) GlobalLock (hBlock);
  751.   *LinePtr = (TypLine far *) ((char far *) *BlockPtr + LineOff);
  752.   if (FindLineID && (*LinePtr)->LineID != FindLineID)
  753.     {
  754.       /* The location specified by hBlock and LineOff does not
  755.        * contain the right line.  So, unlock that block and start
  756.        * scanning the document from the top, looking for the line.
  757.        */
  758.       TypBlock far *MyBlockPtr;
  759.       TypLine far *MyLinePtr;
  760.       HANDLE hMyBlock;
  761.       int MyOffset;
  762.  
  763. #if 0
  764.       MessageBox (hWndConf, "LineID doesn't match", "in LockLine", MB_OK);
  765. #endif
  766.  
  767.       hMyBlock = (*BlockPtr)->OwnerDoc->hFirstBlock;
  768.       MyOffset = sizeof (TypBlock);
  769.       GlobalUnlock (hBlock);
  770.  
  771.       MyBlockPtr = (TypBlock far *) GlobalLock (hMyBlock);
  772.       MyLinePtr = (TypLine far *) ((char far *) MyBlockPtr + sizeof (TypBlock));
  773.  
  774.       while (MyLinePtr->LineID != FindLineID && NextLine (&MyBlockPtr, &MyLinePtr));
  775.  
  776.       if (MyLinePtr->LineID == FindLineID)
  777.     {
  778.       *BlockPtr = MyBlockPtr;
  779.       *LinePtr = MyLinePtr;
  780.     }
  781.       else
  782.     {
  783.       MessageBox (hWndConf, "Can't find line", "in LockLine", MB_ICONHAND | MB_OK);
  784.       return (FALSE);
  785.     }
  786.     }
  787.   return (TRUE);
  788. }
  789.  
  790. /*-- function UnlockLine ---------------------------------------------
  791.  *
  792.  *  Given a block pointer and a line pointer, unlock the block
  793.  *  in memory and return a line ID, a block handle and an offset within the
  794.  *  block to the line.
  795.  *
  796.  *    Entry    BlockPtr    points to a textblock
  797.  *             LinePtr     points to a line in that textblock
  798.  *
  799.  *    Exit     TheLineID   is the LineID of the pointed-to line.
  800.  *             hBlock      is the handle to the textblock.
  801.  *             LineOff     is the offset (in bytes from the beginning
  802.  *                         of the block) of the line.
  803.  */
  804. void
  805. UnlockLine (BlockPtr, LinePtr, hBlock, LineOff, TheLineID)
  806.      TypBlock far *BlockPtr;
  807.      TypLine far *LinePtr;
  808.      HANDLE *hBlock;
  809.      unsigned int *LineOff;
  810.      TypLineID *TheLineID;
  811. {
  812.   PtrToOffset (BlockPtr, LinePtr, hBlock, LineOff, TheLineID);
  813.   GlobalUnlock (*hBlock);
  814. }
  815.  
  816. /*-- function PtrToOffset ---------------------------------------------
  817.  *
  818.  *  Given a block pointer and a line pointer,
  819.  *  return a line ID, a block handle and an offset within the
  820.  *  block to the line.
  821.  *
  822.  *    Entry    BlockPtr    points to a textblock
  823.  *             LinePtr     points to a line in that textblock
  824.  *
  825.  *    Exit     TheLineID   is the LineID of the pointed-to line.
  826.  *             hBlock      is the handle to the textblock.
  827.  *             LineOff     is the offset (in bytes from the beginning
  828.  *                         of the block) of the line.
  829.  */
  830. void
  831. PtrToOffset (BlockPtr, LinePtr, hBlock, LineOff, TheLineID)
  832.      TypBlock far *BlockPtr;
  833.      TypLine far *LinePtr;
  834.      HANDLE *hBlock;
  835.      unsigned int *LineOff;
  836.      TypLineID *TheLineID;
  837. {
  838.   *TheLineID = LinePtr->LineID;
  839.   *LineOff = (char far *) LinePtr - (char far *) BlockPtr;
  840.   *hBlock = BlockPtr->hCurBlock;
  841. }
  842.  
  843. /*-- function WhatLine ------------------------------------------------
  844.  *
  845.  *  Determine the ordinal number of a given line in the document.
  846.  *
  847.  *    Entry    BlockPtr    points to the block containing a line.
  848.  *             LinePtr     points to the line.
  849.  *
  850.  *    Exit     Returns 0 = first line, 1 = second, and so on.
  851.  *
  852.  *  Strategy is to start at the beginning of the document and
  853.  *  scan though the lines, counting lines until we reach the
  854.  *  current line.  In more detail:
  855.  *
  856.  *  Save the current position.
  857.  *  Go to the first block of the document.
  858.  *  Number of lines = 0
  859.  *  While we are not yet at the original block,
  860.  *     Add in the number of lines in this block, just by looking at header.
  861.  *  Now that we have reached the original block, start at the
  862.  *     beginning of the block and scan forward line-by-line until
  863.  *     we reach the original line.
  864.  */
  865. unsigned int
  866. WhatLine (BlockPtr, LinePtr)
  867.      TypBlock far *BlockPtr;
  868.      TypLine far *LinePtr;
  869. {
  870.   unsigned int nLines = 0;
  871.   TypBlock far *MyBlock;
  872.   TypLine far *MyLine;
  873.   TypDoc *MyDoc;
  874.   HANDLE hOrgBlock, hMyBlock, hNewBlock;
  875.   unsigned int OrgOffset, MyOffset;
  876.   TypLineID OrgLineID, MyLineID = 0L;
  877.  
  878.   MyDoc = BlockPtr->OwnerDoc;
  879.   UnlockLine (BlockPtr, LinePtr, &hOrgBlock, &OrgOffset, &OrgLineID);
  880.  
  881.   hMyBlock = MyDoc->hFirstBlock;
  882.   while (hMyBlock != hOrgBlock)
  883.     {
  884.       MyBlock = (TypBlock far *) GlobalLock (hMyBlock);
  885.       nLines += MyBlock->NumLines;
  886.       hNewBlock = MyBlock->hNextBlock;
  887.       if (!hNewBlock)
  888.     {
  889.       MessageBox (MyDoc->hDocWnd, "Hit end of document", "Error in WhatLine",
  890.               MB_OK | MB_ICONHAND);
  891.     }
  892.       GlobalUnlock (hMyBlock);
  893.       hMyBlock = hNewBlock;
  894.     }
  895.  
  896.   /* The technique of scanning the NumLines field starts the        */
  897.   /* counter at 1 rather than 0, so if we got any lines above,      */
  898.   /* adjust the count by decrementing it.                           */
  899.  
  900. /* if(nLines) nLines--; */
  901.  
  902.   /* We have reached the original block.                            */
  903.   /* Start scanning at the first line.                              */
  904.  
  905.   MyOffset = sizeof (TypBlock);
  906.   LockLine (hMyBlock, MyOffset, MyLineID, &MyBlock, &MyLine);
  907.   while (MyOffset != OrgOffset)
  908.     {
  909.       nLines++;
  910.       NextLine (&MyBlock, &MyLine);
  911.       MyOffset = (char far *) MyLine - (char far *) MyBlock;
  912.     }
  913.  
  914.   return (nLines);
  915. }
  916.  
  917. /*--- function NumBlocksInDoc --------------------------------------------
  918.  *
  919.  *  Find the number of blocks in a document.
  920.  *
  921.  * Entry Doc points to a document.
  922.  *
  923.  * Exit  returns the number of textblocks in the document.
  924.  */
  925. int
  926. NumBlocksInDoc (Doc)
  927.      TypDoc *Doc;
  928. {
  929.   TypBlock far *MyBlock;
  930.   HANDLE hMyBlock, hNewBlock;
  931.   int nBlocks = 0;
  932.  
  933.   if (!Doc)
  934.     return (0);
  935.   hMyBlock = Doc->hFirstBlock;
  936.   do
  937.     {
  938.       nBlocks++;
  939.       MyBlock = (TypBlock far *) GlobalLock (hMyBlock);
  940.       hNewBlock = MyBlock->hNextBlock;
  941.       GlobalUnlock (hMyBlock);
  942.       hMyBlock = hNewBlock;
  943.     }
  944.   while (hNewBlock);
  945.  
  946.   return (nBlocks);
  947. }
  948.  
  949. /*--- function FindLineOrd ---------------------------------------------
  950.  *
  951.  *  Find the Nth line in a document.
  952.  *
  953.  *    Entry    Doc      points to a document.
  954.  *             LineOrd  is the ordinal of the line to find.
  955.  *                      0 = first line in document.
  956.  *
  957.  *    Exit     BlockPtr points to the block containing the line.
  958.  *             LinePtr  points to the line.
  959.  *             return value is TRUE iff we found the line.
  960.  */
  961. BOOL
  962. FindLineOrd (Doc, LineOrd, BlockPtr, LinePtr)
  963.      TypDoc *Doc;
  964.      unsigned int LineOrd;
  965.      TypBlock far **BlockPtr;
  966.      TypLine far **LinePtr;
  967. {
  968.   unsigned int LinesSoFar = 0;
  969.   TypBlock far *MyBlockPtr;
  970.   TypLine far *MyLinePtr;
  971.   HANDLE hBlock, hNextBlock;
  972.   int retcode = 0;
  973.  
  974.   hBlock = Doc->hFirstBlock;
  975.   do
  976.     {
  977.       MyBlockPtr = (TypBlock far *) GlobalLock (hBlock);
  978.       if (LinesSoFar + MyBlockPtr->NumLines > LineOrd)
  979.     break;
  980.       LinesSoFar += MyBlockPtr->NumLines;
  981.       hNextBlock = MyBlockPtr->hNextBlock;
  982.       GlobalUnlock (hBlock);
  983.       hBlock = hNextBlock;
  984.     }
  985.   while (hBlock);
  986.  
  987.   if (hBlock)
  988.     {
  989.       MyLinePtr = (TypLine far *) ((char far *) MyBlockPtr + sizeof (TypBlock));
  990.       while (LinesSoFar < LineOrd)
  991.     {
  992.       LinesSoFar++;
  993.       if (!NextLine (&MyBlockPtr, &MyLinePtr))
  994.         break;
  995.     }
  996.       retcode = TRUE;
  997.       *BlockPtr = MyBlockPtr;
  998.       *LinePtr = MyLinePtr;
  999.     }
  1000.   return (retcode);
  1001. }
  1002.  
  1003. /*-- function MoveBytes -----------------------------------------------
  1004.  *
  1005.  *   Move a region of bytes in memory from one place to another.
  1006.  *   Handle overlapping regions without destroying the source.
  1007.  *
  1008.  *    Entry    Source   points to the FWA of the source.
  1009.  *             Target   points to the FWA of the target.
  1010.  *             NumBytes is the number of bytes to copy (>= 0).
  1011.  */
  1012. void
  1013. MoveBytes (FSource, FTarget, NumBytes)
  1014.      void far *FSource, far * FTarget;
  1015.      int NumBytes;
  1016. {
  1017.   char far *Source = FSource;
  1018.   char far *Target = FTarget;
  1019.  
  1020.   if (Source < Target)
  1021.     {
  1022.       Source += NumBytes - 1;
  1023.       Target += NumBytes - 1;
  1024.       while (NumBytes--)
  1025.     *(Target--) = *(Source--);
  1026.     }
  1027.   else
  1028.     {
  1029.       while (NumBytes--)
  1030.     *(Target++) = *(Source++);
  1031.     }
  1032. }
  1033.  
  1034. /*--- function InitDoc ---------------------------------------------------
  1035.  *
  1036.  *  Initialize the fields of a document.
  1037.  */
  1038. int
  1039. InitDoc (Doc, hWnd, Parent, DType)
  1040.      TypDoc *Doc;
  1041.      HWND hWnd;
  1042.      TypDoc *Parent;
  1043.      int DType;
  1044. {
  1045.   TypBlock far *BlockPtr;
  1046.   HANDLE hBlock;
  1047.  
  1048.   Doc->hLastBlock = 0;
  1049.   Doc->TotalLines = 0;
  1050.   Doc->ActiveLines = 0;
  1051.   Doc->BlockSize = BLOCK_SIZE;
  1052.   Doc->SplitSize = (BLOCK_SIZE * 2) / 3;
  1053.   Doc->hDocWnd = hWnd;
  1054.   Doc->hLastSeenBlock = 0;
  1055.   Doc->TopLineOrd = 0;
  1056.   Doc->ParentDoc = Parent;
  1057.   Doc->ParentLineID = 0L;
  1058.   Doc->SearchStr[0] = '\0';
  1059.   Doc->FindLineID = 0L;
  1060.   Doc->TopScLineID = 0L;
  1061.   Doc->InUse = TRUE;
  1062.   Doc->DocType = DType;
  1063.   Doc->hFindBlock = 0;
  1064.  
  1065.   switch (DType)
  1066.     {
  1067.     case DOCTYPE_NET:
  1068.       Doc->OffsetToText = sizeof (TypLine) + sizeof (TypGroup);
  1069.       break;
  1070.     case DOCTYPE_GROUP:
  1071.       Doc->OffsetToText = sizeof (TypLine) + sizeof (TypArticle);
  1072.       break;
  1073.     case DOCTYPE_ARTICLE:
  1074.       Doc->OffsetToText = sizeof (TypLine) + sizeof (TypText);
  1075.       break;
  1076.     }
  1077.  
  1078.   hBlock = GlobalAlloc (GMEM_MOVEABLE, (long) BLOCK_SIZE);
  1079.   if (hBlock)
  1080.     {
  1081.       BlockPtr = (TypBlock far *) GlobalLock (hBlock);
  1082.       SetupEmptyBlock (BlockPtr, hBlock, 0, 0, Doc);
  1083.  
  1084.       Doc->hFirstBlock = hBlock;
  1085.       Doc->hLastBlock = hBlock;
  1086.       Doc->hCurAddBlock = hBlock;
  1087.       Doc->AddOffset = sizeof (TypBlock);
  1088.       Doc->AddLineID = 0L;
  1089.       Doc->hCurTopScBlock = hBlock;
  1090.       Doc->TopScOffset = sizeof (TypBlock);
  1091.       Doc->TopScLineID = 0L;
  1092.       Doc->LastSeenLineID = 0L;
  1093.  
  1094.       GlobalUnlock (hBlock);
  1095.     }
  1096.   else
  1097.     {
  1098.       MessageBox (hWnd, "Could not allocate textblock", "Out of Memory Error", MB_OK | MB_ICONHAND);
  1099.     }
  1100.  
  1101.   return (0);
  1102. }
  1103.  
  1104. /*-- function FreeDoc ----------------------------------------------
  1105.  *
  1106.  *  Free up all the text blocks associated with a document.
  1107.  *
  1108.  *    Entry    Doc   points to the document in question.
  1109.  */
  1110. void
  1111. FreeDoc (Doc)
  1112.      TypDoc *Doc;
  1113. {
  1114.   TypBlock far *BlockPtr;
  1115.   HANDLE hBlock, hNextBlock;
  1116.  
  1117.   /* Start at the first block of the document, and travel           */
  1118.   /* down the linked list of blocks, freeing them.                  */
  1119.  
  1120.   hBlock = Doc->hFirstBlock;
  1121.   while (hBlock)
  1122.     {
  1123.       BlockPtr = (TypBlock far *) GlobalLock (hBlock);
  1124.       hNextBlock = BlockPtr->hNextBlock;
  1125.       GlobalUnlock (hBlock);
  1126.       GlobalFree (hBlock);
  1127.       hBlock = hNextBlock;
  1128.     }
  1129. }
  1130.  
  1131. /*--- function ForAllLines ---------------------------------------------
  1132.  *
  1133.  *  Perform an operation for all lines in a document.  The operation
  1134.  *  to be performed is specified by a C function argument.
  1135.  *
  1136.  *    Entry Doc            is the document.
  1137.  *          lpfnFunc       is a pointer to the function to call for
  1138.  *                         each line.
  1139.  *          lFlag          is a flag that's passed to the function.
  1140.  *
  1141.  */
  1142. void
  1143. ForAllLines (TypDoc *Doc, 
  1144.        void lpfnFunc(TypDoc *Doc, TypBlock far ** BlockPtr, TypLine far ** LinePtr, int wFlag, int wValue),
  1145.        int wFlag, int wValue)
  1146. {
  1147.   TypBlock far *BlockPtr;
  1148.   TypLine far *LinePtr;
  1149.   TypLineID old_lineID;
  1150.   BOOL looping = TRUE;
  1151.  
  1152.  
  1153.   TopOfDoc (Doc, &BlockPtr, &LinePtr);
  1154.   if (LinePtr->length != END_OF_BLOCK)
  1155.     {
  1156.       do
  1157.     {
  1158.       old_lineID = LinePtr->LineID;
  1159.       lpfnFunc (Doc, &BlockPtr, &LinePtr, wFlag, wValue);
  1160.       if (old_lineID == LinePtr->LineID &&
  1161.           LinePtr->length != END_OF_BLOCK)
  1162.         {
  1163.           looping = NextLine (&BlockPtr, &LinePtr);
  1164.         }
  1165.       else if (LinePtr->length == END_OF_BLOCK)
  1166.         {
  1167.           looping = FALSE;
  1168.         }
  1169.     }
  1170.       while (looping);
  1171.     }
  1172. /*   UnlockLine (BlockPtr, LinePtr,
  1173.     &(FindDoc->hFindBlock), &(FindDoc->FindOffset), &(FindDoc->FindLineID)); */
  1174.  
  1175. }
  1176.  
  1177.  
  1178. /*--- function FindString ----------------------------------------------
  1179.  *
  1180.  *   Locate a search string in a document.
  1181.  *
  1182.  *   Entry  StartAtTop        is TRUE iff we should start the search at
  1183.  *                            the top of the document.
  1184.  *          FindDoc           points to the document in which we are searching.
  1185.  *          ->hFindBlock      is the block to start at, if StartAtTop
  1186.  *                            is FALSE.
  1187.  *          ->FindOffset      is the offset within the block of the
  1188.  *                            line to start at, if StartAtTop is FALSE.
  1189.  *          ->SearchStr       has the string to search for.
  1190.  *
  1191.  *   Exit   returns -1 if the string was not found,
  1192.  *            else the offset of the string from the beginning of the line.
  1193.  *          FindDoc ...
  1194.  *          ->hFindBlock      has the block handle of the line which
  1195.  *                            was found (if any)
  1196.  *          ->FindOffset      has the offset of the found line (if any)
  1197.  *
  1198.  */
  1199. int
  1200. FindString (StartAtTop)
  1201.      BOOL StartAtTop;
  1202. {
  1203.   TypBlock far *BlockPtr;
  1204.   TypLine far *LinePtr;
  1205.   HANDLE hBlock;
  1206.   unsigned int Offset;
  1207.   unsigned int TextOffset;
  1208.   BOOL found = -1;
  1209.   int TargLen = 0;
  1210.   int SourceLen;
  1211.   char *Target;
  1212.   char sourceline[MAXINTERNALLINE];
  1213.   char targline[MAXFINDSTRING];
  1214.   char *sourceptr, far * orglineptr;
  1215.   char *targptr;
  1216.   TypLineID MyLineID;
  1217.   register char ch;
  1218.  
  1219.   if (StartAtTop)
  1220.     {
  1221.       hBlock = FindDoc->hFirstBlock;
  1222.       Offset = sizeof (TypBlock);
  1223.       MyLineID = 0L;
  1224.     }
  1225.   else
  1226.     {
  1227.       hBlock = FindDoc->hFindBlock;
  1228.       Offset = FindDoc->FindOffset;
  1229.       MyLineID = FindDoc->FindLineID;
  1230.     }
  1231.   LockLine (hBlock, Offset, MyLineID, &BlockPtr, &LinePtr);
  1232.   /* If doing a Find Next, skip forward one line before starting    */
  1233.   /* the search.                                                    */
  1234.   if (!StartAtTop)
  1235.     {
  1236.       NextLine (&BlockPtr, &LinePtr);
  1237.     }
  1238.  
  1239.   TextOffset = FindDoc->OffsetToText;
  1240.  
  1241.   Target = FindDoc->SearchStr;
  1242.   for (targptr = targline; ch = *Target, *(targptr++) = tolower (ch);
  1243.        TargLen++)
  1244.     Target++;
  1245.  
  1246.   if (LinePtr->length != END_OF_BLOCK)
  1247.     {
  1248.       do
  1249.     {
  1250.       orglineptr = (char far *) LinePtr + TextOffset;
  1251.       sourceptr = sourceline;
  1252.       for (SourceLen = 0; ch = *(orglineptr),
  1253.            *(sourceptr++) = tolower (ch); SourceLen++)
  1254.         orglineptr++;
  1255.       found = SearchLine (sourceline, SourceLen, targline, TargLen);
  1256.     }
  1257.       while (found == -1 && NextLine (&BlockPtr, &LinePtr));
  1258.     }
  1259.   UnlockLine (BlockPtr, LinePtr,
  1260.     &(FindDoc->hFindBlock), &(FindDoc->FindOffset), &(FindDoc->FindLineID));
  1261.   return (found);
  1262. }
  1263.  
  1264. /*--- function SearchLine -----------------------------------------------
  1265.  *
  1266.  *  Search a line for a target string.
  1267.  *
  1268.  *  Entry   Line     is a zero-terminated string to search.
  1269.  *          LineLen  is strlen(Line).  Redundant, but passed for
  1270.  *                   efficiency.
  1271.  *          Target   is the Target string to search for, zero-terminated.
  1272.  *          TargLen  is strlen(Target), passed for efficiency.
  1273.  *
  1274.  *  Exit    returns  -1 if not found, else the character position
  1275.  *                   in which the string was found (0=first).
  1276.  */
  1277. int
  1278. SearchLine (Line, LineLen, Target, TargLen)
  1279.      char *Line;
  1280.      int LineLen;
  1281.      char *Target;
  1282.      int TargLen;
  1283. {
  1284.   char *stopptr = Line + LineLen - TargLen + 1;
  1285.   char *lineptr;
  1286.   char *searchptr;
  1287.   char *targptr;
  1288.  
  1289.   if (LineLen <= 0 || TargLen <= 0 || TargLen > LineLen)
  1290.     return (-1);
  1291.  
  1292.   for (lineptr = Line; lineptr != stopptr; lineptr++)
  1293.     {
  1294.       searchptr = lineptr;
  1295.       for (targptr = Target; *targptr && *(targptr) == *(searchptr);)
  1296.     {
  1297.       targptr++;
  1298.       searchptr++;
  1299.     }
  1300.       if (!(*targptr))
  1301.     {
  1302.       return (lineptr - Line);
  1303.     }
  1304.     }
  1305.   return (-1);
  1306. }
  1307.  
  1308. /*--- function DoFind ---------------------------------------------------
  1309.  *
  1310.  *  Controlling routine for searching for text.
  1311.  *  Takes care of displaying window properly when search is done.
  1312.  *
  1313.  *    Entry    StartAtTop  is TRUE iff we should start at the top
  1314.  *                         of the document.
  1315.  *             FindDoc     points to the document being searched.
  1316.  *                         All the info we need is in fields in this doc.
  1317.  */
  1318. BOOL
  1319. DoFind (StartAtTop)
  1320.      BOOL StartAtTop;
  1321. {
  1322.   int CharPos;
  1323.   int iline;
  1324.   int X, Y;
  1325.   HDC hDC;
  1326.   TypBlock far *BlockPtr;
  1327.   TypLine far *LinePtr;
  1328.   HANDLE hBlock;
  1329.   unsigned int Offset;
  1330.   char far *textptr;
  1331.   POINT point;
  1332.   int mylen;
  1333.   int found = FALSE;
  1334.   BOOL refresh = FALSE;
  1335.   int goline;
  1336.   unsigned int LineOrd;
  1337.   unsigned int LastAllowedLineOrd;
  1338.   TypLineID MyLineID;
  1339.  
  1340.   CharPos = FindString (StartAtTop);
  1341.   if (CharPos >= 0)
  1342.     {
  1343.       iline = LineOnScreen (FindDoc, FindDoc->hFindBlock, FindDoc->FindOffset, FindDoc->FindLineID);
  1344.       /* If this line wasn't on the screen, we are going to have
  1345.        * to adjust the top of the screen.
  1346.        * Make the found line the top of the screen, then back up a
  1347.        * little to give the user a context in which to view the line.
  1348.        */
  1349.       if (iline == -1)
  1350.     {
  1351.       FindDoc->hCurTopScBlock = FindDoc->hFindBlock;
  1352.       FindDoc->TopScOffset = FindDoc->FindOffset;
  1353.       FindDoc->TopScLineID = FindDoc->FindLineID;
  1354.       refresh = TRUE;
  1355.       LockLine (FindDoc->hCurTopScBlock, FindDoc->TopScOffset, FindDoc->TopScLineID,
  1356.             &BlockPtr, &LinePtr);
  1357.       for (goline = FindDoc->ScYLines / 4; goline; goline--)
  1358.         {
  1359.           PrevLine (&BlockPtr, &LinePtr);
  1360.         }
  1361.       /* Have we gone past the top of the last screen?
  1362.            * If so, move the top line back to the top of the last screen.
  1363.            */
  1364.       LineOrd = WhatLine (BlockPtr, LinePtr);
  1365.       LastAllowedLineOrd = FindDoc->TotalLines - FindDoc->ScYLines;
  1366.       if (LineOrd > LastAllowedLineOrd)
  1367.         {
  1368.           GlobalUnlock (BlockPtr->hCurBlock);
  1369.           FindLineOrd (FindDoc, LastAllowedLineOrd, &BlockPtr, &LinePtr);
  1370.           LineOrd = LastAllowedLineOrd;
  1371.         }
  1372.       UnlockLine (BlockPtr, LinePtr, &(FindDoc->hCurTopScBlock),
  1373.               &(FindDoc->TopScOffset), &(FindDoc->TopScLineID));
  1374.       FindDoc->TopLineOrd = LineOrd;
  1375.       iline = LineOnScreen (FindDoc, FindDoc->hFindBlock, FindDoc->FindOffset, FindDoc->FindLineID);
  1376.     }
  1377. #if 0
  1378.  
  1379.       LockLine (FindDoc->hFindBlock, FindDoc->FindOffset, FindDoc->FindLineID,
  1380.         &BlockPtr, &LinePtr);
  1381.       textptr = (char far *) LinePtr + FindDoc->OffsetToText;
  1382.       hDC = GetDC (FindDoc->hDocWnd);
  1383.       SelectObject (hDC, hFont);
  1384.       point.x = SideSpace + LOWORD (GetTextExtent (hDC, textptr, CharPos));
  1385.       ReleaseDC (FindDoc->hDocWnd, hDC);
  1386.       UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
  1387.  
  1388.       point.y = TopSpace + iline * LineHeight + 4 * LineHeight / 5;
  1389.       ClientToScreen (FindDoc->hDocWnd, &point);
  1390.       SetCursorPos (point.x, point.y);
  1391. #endif
  1392.  
  1393.       InvalidateRect (FindDoc->hDocWnd, NULL, FALSE);
  1394.       found = TRUE;
  1395.     }
  1396.   return (found);
  1397. }
  1398.